#pragma once

#include "Terrain.h"
#include "Maths.h"

const HashedString Terrain::type("Terrain");

Terrain::Terrain()
{
}

Terrain::Terrain( Pointer<Heightmap>& heightmap ) : m_heightmap(heightmap)
{
	Initialize(m_heightmap);
}

void Terrain::Initialize( Pointer<Heightmap>& heightmap )
{
	m_heightmap = heightmap;
	unsigned int& resolution = heightmap->getResolution();
	unsigned int& pixelStep = heightmap->getPixelStep();
	float center[3] =	{ -(float)resolution/2, 0, -(float)resolution/2 };
	float X, Z;
	int tNormIndex = 0, texIndex = 0, vertIndex = 0, normIndex = 0;
	unsigned int numberOfVertices = (int) ((resolution*resolution*6)/(pixelStep * pixelStep));
	std::vector<float> triNormals(numberOfVertices*3);
	HeightmapVertex::Vector_t vertices(numberOfVertices);

	for (unsigned int z = 0; z < resolution; z += pixelStep)
	{
		for (unsigned int x = 0; x < resolution; x += pixelStep)
		{
			for (int tri = 0; tri < 6; ++tri)
			{
				X = (float) x + ( ( tri == 1 || tri == 2 || tri == 5 ) ? pixelStep : 0.0f );
				Z = (float) z + ( ( tri == 1 || tri == 4 || tri == 5 ) ? pixelStep : 0.0f );

				vertices[vertIndex].x = (X + center[0]) * heightmap->getWidthScale();
				vertices[vertIndex].y = ((heightmap->Height((int)X, (int)Z) - 127.5f) *  heightmap->getHeightScale());
				vertices[vertIndex].z = (Z + center[2]) * heightmap->getLengthScale();
				
				vertices[vertIndex].u = X / resolution;
				vertices[vertIndex].v = Z / resolution;

				++vertIndex;

				if(tri == 2 || tri == 5) 
				{
					float vector[2][3];
					float norm[3];

					for (int temp = 0; temp < 2; ++temp)
					{
						vector[temp][0] = vertices[vertIndex-1-temp].x - vertices[vertIndex-3].x;
						vector[temp][1] = vertices[vertIndex-1-temp].y - vertices[vertIndex-3].y;
						vector[temp][2] = vertices[vertIndex-1-temp].z - vertices[vertIndex-3].z;
					}

					Maths::CrossProduct( norm, vector[1], vector[0] );
					Maths::Normalize( norm );
					triNormals[tNormIndex++] = norm[0];
					triNormals[tNormIndex++] = norm[1];
					triNormals[tNormIndex++] = norm[2];
				}
			}
		}
	}

	for (unsigned int z = 0; z < resolution/(int)pixelStep; ++z)
	{
		for (unsigned int x = 0; x < resolution/(int)pixelStep; ++x)
		{
			for( int nTri = 0; nTri < 6; nTri++ )
			{
				int count = 0;
				X = x + ( ( nTri == 1 || nTri == 2 || nTri == 5 ) ? 1 : 0 );
				Z = z + ( ( nTri == 1 || nTri == 4 || nTri == 5 ) ? 1 : 0 );
				
				if( X < resolution/(int)pixelStep && Z < resolution/(int)pixelStep )
				{
					vertices[normIndex].nx += triNormals[3*((X+(Z*resolution/(int)pixelStep))*2)];
					vertices[normIndex].ny += triNormals[3*((X+(Z*resolution/(int)pixelStep))*2) + 1];
					vertices[normIndex].nz += triNormals[3*((X+(Z*resolution/(int)pixelStep))*2) + 2];

					vertices[normIndex].nx += triNormals[3*(((X)+(Z*resolution/(int)pixelStep))*2+1)];
					vertices[normIndex].ny += triNormals[3*(((X)+(Z*resolution/(int)pixelStep))*2+1) + 1];
					vertices[normIndex].nz += triNormals[3*(((X)+(Z*resolution/(int)pixelStep))*2+1) + 2];
					count += 2;
				}
				if( X > 0 )
				{
					if(Z > 0)
					{
						vertices[normIndex].nx += triNormals[3*(((X-1)+((Z-1)*resolution/(int)pixelStep))*2)];
						vertices[normIndex].ny += triNormals[3*(((X-1)+((Z-1)*resolution/(int)pixelStep))*2)+1];
						vertices[normIndex].nz += triNormals[3*(((X-1)+((Z-1)*resolution/(int)pixelStep))*2)+2];

						vertices[normIndex].nx += triNormals[3*(((X-1)+((Z-1)*resolution/(int)pixelStep))*2 + 1)];
						vertices[normIndex].ny += triNormals[3*(((X-1)+((Z-1)*resolution/(int)pixelStep))*2 + 1)+1];
						vertices[normIndex].nz += triNormals[3*(((X-1)+((Z-1)*resolution/(int)pixelStep))*2 + 1)+2];
						count += 2;
					}
					if(Z < resolution/(int)pixelStep)
					{
						vertices[normIndex].nx += triNormals[3*(((X-1)+(Z*resolution/(int)pixelStep))*2+1)];
						vertices[normIndex].ny += triNormals[3*(((X-1)+(Z*resolution/(int)pixelStep))*2+1)+1];
						vertices[normIndex].nz += triNormals[3*(((X-1)+(Z*resolution/(int)pixelStep))*2+1)+2];
						++count;
					}
				}
				if( X < resolution/(int)pixelStep && Z > 0 )
				{
					vertices[normIndex].nx += triNormals[3*(((X)+((Z-1)*resolution/(int)pixelStep))*2)];
					vertices[normIndex].nx += triNormals[3*(((X)+((Z-1)*resolution/(int)pixelStep))*2)+1];
					vertices[normIndex].nx += triNormals[3*(((X)+((Z-1)*resolution/(int)pixelStep))*2)+2];
					++count;
				}
				Maths::Normalize(&vertices[normIndex].nx);
				++normIndex;
			}
		}
	}

	// Build quadtree here
	Material mat;
	mat.m_shininess = 1;
	mat.m_shader = HashedString("nil");
	VertexContainer<HeightmapVertex>* ptr;
	Pointer<IVertexContainer> vertexContainer = ptr = new VertexContainer<HeightmapVertex>(VertexDeclaration(3, VertexType(0, 3, enumPosition), VertexType(12, 3, enumNormal), VertexType(24, 2, enumTexCoord0)));
	ptr->resize(vertices.size());
	memcpy(vertexContainer->getVertexPointer(), &vertices[0], sizeof(HeightmapVertex) * vertices.size());
	
	Pointer<Mesh> mesh(new Mesh(vertexContainer));
	mesh->getMaterial() = mat;
	m_meshes.push_back(mesh);
}

float Terrain::getWidth()
{
	return m_heightmap->getResolution();
}

float Terrain::getLength()
{
	return m_heightmap->getResolution();
}

float Terrain::getTrueHeight( float x, float y )
{
	return m_heightmap->InterpolatedHeight(x, y);
}

void Terrain::setTexture()
{
}

const HashedString& Terrain::Type()
{
	return type;
}